﻿using System;
using System.Collections.Generic;
using ComponentSystem;
using Cysharp.Threading.Tasks;
using YooAssetsSystem.Patch;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.U2D;
using YooAsset;

namespace YooAssetsSystem
{
    /// <summary>
    /// YooAssets服务实体
    /// 提供好用的加载接口
    /// </summary>
    public partial class YAService_Entity : EntityCSharp
    {
        /// <summary>
        /// 单例
        /// </summary>
        public static YAService_Entity Instance = null;

        /// <summary>
        /// YooAssetsSystem实体
        /// </summary>
        public YooAssetsSystem_Entity yooAssetsSystem = null;

        /// <summary>
        /// 句柄缓存组件
        /// </summary>
        private AssetsHandleCache_Com handleCache_Com =>
            this.yooAssetsSystem.GetComponent<AssetsHandleCache_Com>();

        /// <summary>
        /// 资源加载组件
        /// </summary>
        private AssetsLoad_Com assetsLoad_Com =>
            this.yooAssetsSystem.GetComponent<AssetsLoad_Com>();
    }

    public partial class YAService_Entity
    {
        protected override void Awake()
        {
            if (Instance != null) return;

            Instance = this;
            yooAssetsSystem = new YooAssetsSystem_Entity();
            base.Awake();
        }

        #region 包下载

        /// <summary>
        /// 下载包
        /// </summary>
        /// <param name="playMode">下载模式</param>
        /// <param name="url">下载地址</param>
        /// <param name="appVersion">版本号</param>
        /// <param name="packageName">包名</param>
        /// <param name="isDefaultPackage">是否是默认包</param>
        /// <param name="loadTags">下载标签 默认:全部下载</param>
        /// <param name="isCustomizeUrl">是否自定义url</param>
        /// <param name="ChangeStateEvent">状态改变的事件</param>
        /// <param name="PatchDoneEvent">补丁下载完成的事件</param>
        /// <param name="UpdateFailedEvent">更新失败的事件</param>
        /// <param name="NeedDownloadEvent">需要下载的事件</param>
        public async UniTaskVoid DownloadPackage(EPlayMode playMode, string url, string appVersion, string packageName, bool isDefaultPackage, string[] loadTags, bool isCustomizeUrl, UnityAction<string> ChangeStateEvent, UnityAction<string> PatchDoneEvent, UnityAction<string> UpdateFailedEvent, UnityAction<int, long, UnityAction> NeedDownloadEvent)
        {
            PatchSeveice.PatchConfig config = new PatchSeveice.PatchConfig();
            config.playMode = playMode;
            config.updateResUrl = url;
            config.appVersion = appVersion;
            config.isDefaultPackage = isDefaultPackage;
            config.multiMaxNumber = 10;
            config.failedTryAgain = 3;
            config.loadTags = loadTags;
            config.isCustomizeUrl = isCustomizeUrl;

            PatchSeveice patchSeveice = new PatchSeveice(packageName, config);

            patchSeveice.Event.ChangeStateEvent += ChangeStateEvent;
            patchSeveice.Event.PatchDoneEvent += PatchDoneEvent;
            patchSeveice.Event.UpdateFailedEvent += UpdateFailedEvent;
            patchSeveice.Event.NeedDownloadEvent += NeedDownloadEvent;

            patchSeveice.Run();

            await UniTask.CompletedTask;
        }

        #endregion

        #region 功能函数

        /// <summary>
        /// 检查资源定位地址是否有效
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public bool CheckLocationValid(string assetName, string pkgName = "", UnityAction<bool> act = null)
            => assetsLoad_Com.CheckLocationValid(assetName, pkgName, act);

        #endregion

        #region 场景 加载/卸载

        /// <summary>
        /// 同步加载 场景
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="loadMode">加载模式</param>
        /// <param name="processing">进度回调</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="onComplete">完成回调</param>
        /// <returns></returns>
        public Scene LoadScene_Sync(string assetName, LoadSceneMode loadMode = default, UnityAction<float> processing = null, string pkgName = "", UnityAction<Scene> onComplete = null)
            => assetsLoad_Com.LoadScene_Sync(assetName, loadMode, processing, pkgName, onComplete);

        /// <summary>
        /// 异步加载 场景
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="loadMode">加载模式</param>
        /// <param name="processing">进度回调</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="onComplete">完成回调</param>
        /// <returns></returns>
        public async UniTask<Scene> LoadScene_Async(string assetName, LoadSceneMode loadMode = default, UnityAction<float> processing = null, string pkgName = "", UnityAction<Scene> onComplete = null)
            => await assetsLoad_Com.LoadScene_Async(assetName, loadMode, processing, pkgName, onComplete);

        /// <summary>
        /// 异步卸载 场景
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <returns></returns>
        public async UniTask<bool> UnLoadScene_Async(string assetName, string pkgName = "")
            => await assetsLoad_Com.UnLoadScene_Async(assetName, pkgName);

        #endregion

        #region 资源 加载


        /// <summary>
        /// GameObject 同步加载和实例化
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="active">是否激活</param>
        /// <param name="parent">需要挂载的父节点</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns>Unity3d GameObject对象</returns>
        /// <exception cref="TypeLoadException"></exception>
        public GameObject LoadGameObject_Sync(string assetName, bool active = true, Transform parent = null, string pkgName = "", UnityAction<GameObject> act = null)
        {
            if (!typeof(GameObject).IsSubclassOf(typeof(UnityEngine.Object)))
                throw new TypeLoadException($"资源加载了不支持的资源类型 {typeof(GameObject).Name} -- pkgName: {pkgName} -- assetName: {assetName}");

            AssetHandle assetHandle = null;

            if (string.IsNullOrEmpty(pkgName)) assetHandle = YooAssets.LoadAssetSync<GameObject>(assetName);
            else
            {
                ResourcePackage package = assetsLoad_Com.LoadPackage(pkgName);
                assetHandle = package.LoadAssetSync<GameObject>(assetName);
            }

            if (assetHandle.Status != EOperationStatus.Succeed)
            {
                assetsLoad_Com.HandleErrToDispose(assetHandle, pkgName, assetName);
                return null;
            }

            this.handleCache_Com.AddHandle(assetHandle, pkgName, assetName);

            GameObject loadedAsset = assetHandle.InstantiateSync(parent);

            if (loadedAsset != null) loadedAsset.SetActive(active);

            act?.Invoke(loadedAsset);

            return loadedAsset;
        }

        /// <summary>
        /// GameObject 异步加载和实例化
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="active">是否激活</param>
        /// <param name="parent">需要挂载的父节点</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns>Unity3d GameObject对象</returns>
        /// <exception cref="TypeLoadException"></exception>
        public async UniTask<GameObject> LoadGameObject_Async(string assetName, bool active = true, Transform parent = null, string pkgName = "", UnityAction<GameObject> act = null)
        {
            if (!typeof(GameObject).IsSubclassOf(typeof(UnityEngine.Object)))
                throw new TypeLoadException($"资源加载了不支持的资源类型 {typeof(GameObject).Name} -- pkgName: {pkgName} -- assetName: {assetName}");

            AssetHandle assetHandle = null;

            if (string.IsNullOrEmpty(pkgName)) assetHandle = YooAssets.LoadAssetAsync<GameObject>(assetName);
            else
            {
                ResourcePackage package = assetsLoad_Com.LoadPackage(pkgName);
                assetHandle = package.LoadAssetAsync<GameObject>(assetName);
            }

            await assetHandle.ToUniTask();

            if (assetHandle.Status != EOperationStatus.Succeed)
            {
                assetsLoad_Com.HandleErrToDispose(assetHandle, pkgName, assetName);
                return null;
            }

            this.handleCache_Com.AddHandle(assetHandle, pkgName, assetName);

            InstantiateOperation instantiateOperation = assetHandle.InstantiateAsync(parent, active);

            await instantiateOperation.ToUniTask();

            GameObject loadedAsset = instantiateOperation.Result;

            act?.Invoke(loadedAsset);

            return loadedAsset;
        }

        /// <summary>
        /// 同步加载 通用加载 指定包中的指定资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assetName"></param>
        /// <param name="pkgName"></param>
        /// <param name="act"></param>
        /// <returns></returns>
        public T LoadAssets_Sync<T>(string assetName, string pkgName = "",
            UnityAction<T> act = null) where T : UnityEngine.Object => assetsLoad_Com.LoadAssets_Sync<T>(assetName, pkgName, act);

        /// <summary>
        /// 异步加载 通用加载 指定包中的指定资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assetName"></param>
        /// <param name="pkgName"></param>
        /// <param name="act"></param>
        /// <returns></returns>
        public async UniTask<T> LoadAssets_Async<T>(string assetName, string pkgName = "",
            UnityAction<T> act = null) where T : UnityEngine.Object => await assetsLoad_Com.LoadAssets_Async<T>(assetName, pkgName, act);
        
        /// <summary>
        /// 加载原始文件（提前用异步方法加载AssetBundle，并让其驻留在内存中，然后业务层就可以用同步方法去加载其中的资源对象。）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <returns></returns>
        public async UniTask LoadRawFileAsync<T>(string assetName, string pkgName = "") where T : UnityEngine.Object
        {
            await assetsLoad_Com.LoadRawFileAsync<T>(assetName, pkgName);
        }

        /// <summary>
        /// Prefab 同步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public GameObject LoadPrefab_Sync(string assetName, string pkgName = "",
            UnityAction<GameObject> act = null) => assetsLoad_Com.LoadAssets_Sync(assetName, pkgName, act);
        /// <summary>
        /// Prefab 异步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public async UniTask<GameObject> LoadPrefab_Async(string assetName, string pkgName = "",
            UnityAction<GameObject> act = null) => await assetsLoad_Com.LoadAssets_Async(assetName, pkgName, act);

        /// <summary>
        /// TextAsset 同步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public TextAsset LoadTextAsset_Sync(string assetName, string pkgName = "",
            UnityAction<TextAsset> act = null) => assetsLoad_Com.LoadAssets_Sync(assetName, pkgName, act);
        /// <summary>
        /// TextAsset 异步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public async UniTask<TextAsset> LoadTextAsset_Async(string assetName, string pkgName = "",
            UnityAction<TextAsset> act = null) => await assetsLoad_Com.LoadAssets_Async(assetName, pkgName, act);

        /// <summary>
        /// Sprites 同步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public List<Sprite> LoadSprites_Sync(string assetName, string pkgName = "",
            UnityAction<List<Sprite>> act = null) => assetsLoad_Com.LoadSubAssets_Sync<Sprite>(assetName, pkgName, act);

        /// <summary>
        /// Sprites 异步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public async UniTask<List<Sprite>> LoadSprites_Async(string assetName, string pkgName = "",
            UnityAction<List<Sprite>> act = null) => await assetsLoad_Com.LoadSubAssets_Async<Sprite>(assetName, pkgName, act);

        /// <summary>
        /// Sprite 异步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public async UniTask<Sprite> LoadSprite_Async(string assetName, string pkgName = "",
            UnityAction<Sprite> act = null) => await assetsLoad_Com.LoadAssets_Async<Sprite>(assetName, pkgName, act);

        /// <summary>
        /// SpriteAtlas 同步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public SpriteAtlas LoadSpriteAtlas_Sync(string assetName, string pkgName = "",
            UnityAction<SpriteAtlas> act = null) => assetsLoad_Com.LoadAssets_Sync<SpriteAtlas>(assetName, pkgName, act);

        /// <summary>
        /// SpriteAtlas 异步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public async UniTask<SpriteAtlas> LoadSpriteAtlas_Async(string assetName, string pkgName = "",
            UnityAction<SpriteAtlas> act = null) => await assetsLoad_Com.LoadAssets_Async<SpriteAtlas>(assetName, pkgName, act);

        /// <summary>
        /// 加载Texture2D
        /// </summary>
        /// <param name="assetName"></param>
        /// <param name="pkgName"></param>
        /// <param name="act"></param>
        /// <returns></returns>
        public async UniTask<Texture2D> LoadTexture2D_Async(string assetName, string pkgName = "",
            UnityAction<Texture2D> act = null) => await assetsLoad_Com.LoadAssets_Async<Texture2D>(assetName, pkgName, act);

        /// <summary>
        /// AudioClip 同步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public AudioClip LoadAudioClip_Sync(string assetName, string pkgName = "",
            UnityAction<AudioClip> act = null) => assetsLoad_Com.LoadAssets_Sync<AudioClip>(assetName, pkgName, act);

        /// <summary>
        /// AudioClip 异步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public async UniTask<AudioClip> LoadAudioClip_Async(string assetName, string pkgName = "",
            UnityAction<AudioClip> act = null) => await assetsLoad_Com.LoadAssets_Async<AudioClip>(assetName, pkgName, act);

        /// <summary>
        /// AnimationClip 同步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public AnimationClip LoadAnimationClip_Sync(string assetName, string pkgName = "",
            UnityAction<AnimationClip> act = null) => assetsLoad_Com.LoadAssets_Sync<AnimationClip>(assetName, pkgName, act);

        /// <summary>
        /// AnimationClip 异步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public async UniTask<AnimationClip> LoadAnimationClip_Async(string assetName, string pkgName = "",
            UnityAction<AnimationClip> act = null) => await assetsLoad_Com.LoadAssets_Async<AnimationClip>(assetName, pkgName, act);

        /// <summary>
        /// ScriptableObject 同步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public ScriptableObject LoadScriptableObject_Sync(string assetName, string pkgName = "",
            UnityAction<ScriptableObject> act = null) => assetsLoad_Com.LoadAssets_Sync<ScriptableObject>(assetName, pkgName, act);

        /// <summary>
        /// ScriptableObject 异步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public async UniTask<ScriptableObject> LoadScriptableObject_Async(string assetName, string pkgName = "",
            UnityAction<ScriptableObject> act = null) => await assetsLoad_Com.LoadAssets_Async<ScriptableObject>(assetName, pkgName, act);

        /// <summary>
        /// Material 同步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public Material LoadMaterial_Sync(string assetName, string pkgName = "",
            UnityAction<Material> act = null) => assetsLoad_Com.LoadAssets_Sync<Material>(assetName, pkgName, act);

        /// <summary>
        /// Material 异步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public async UniTask<Material> LoadMaterial_Async(string assetName, string pkgName = "",
            UnityAction<Material> act = null) => await assetsLoad_Com.LoadAssets_Async<Material>(assetName, pkgName, act);

        /// <summary>
        /// Shader 同步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public Shader LoadShader_Sync(string assetName, string pkgName = "",
            UnityAction<Shader> act = null) => assetsLoad_Com.LoadAssets_Sync<Shader>(assetName, pkgName, act);

        /// <summary>
        /// Shader 异步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public async UniTask<Shader> LoadShader_Async(string assetName, string pkgName = "",
            UnityAction<Shader> act = null) => await assetsLoad_Com.LoadAssets_Async<Shader>(assetName, pkgName, act);

        /// <summary>
        /// Dll 同步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public TextAsset LoadDll_Sync(string assetName, string pkgName = "",
            UnityAction<TextAsset> act = null) => assetsLoad_Com.LoadAssets_Sync(assetName, pkgName, act);
        /// <summary>
        /// Dll 异步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public async UniTask<TextAsset> LoadDll_Async(string assetName, string pkgName = "",
            UnityAction<TextAsset> act = null) => await assetsLoad_Com.LoadAssets_Async(assetName, pkgName, act);

        /// <summary>
        /// TMPFont 同步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public TMP_FontAsset LoadTMPFont_Sync(string assetName, string pkgName = "",
            UnityAction<TMP_FontAsset> act = null) => assetsLoad_Com.LoadAssets_Sync(assetName, pkgName, act);
        /// <summary>
        /// TMPFont 异步加载
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <returns></returns>
        public async UniTask<TMP_FontAsset> LoadTMPFont_Async(string assetName, string pkgName = "",
            UnityAction<TMP_FontAsset> act = null) => await assetsLoad_Com.LoadAssets_Async(assetName, pkgName, act);

        /// <summary>
        /// 加载Font字体
        /// </summary>
        /// <param name="assetName"></param>
        /// <param name="pkgName"></param>
        /// <param name="act"></param>
        /// <returns></returns>
        public async UniTask<Font> LoadFont_Async(string assetName, string pkgName = "",
            UnityAction<Font> act = null) => await assetsLoad_Com.LoadAssets_Async(assetName, pkgName, act);

        #endregion

        #region 加载批量资源

        /// <summary>
        /// 同步加载 通用加载 资源包中指定路径的指定类型所有资源
        /// </summary>
        /// <param name="assetPath">资源路径</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <typeparam name="T">资源类型</typeparam>
        /// <returns></returns>
        /// <exception cref="TypeLoadException"></exception>
        public List<TextAsset> LoadAllOneAssets_Sync<T>(string assetPath, string pkgName = "",
            UnityAction<List<TextAsset>> act = null) => assetsLoad_Com.LoadAllOneAssets_Sync<TextAsset>(assetPath, pkgName, act);

        /// <summary>
        /// 异步加载 通用加载 资源包中指定路径的指定类型所有资源
        /// </summary>
        /// <param name="assetPath">资源路径</param>
        /// <param name="pkgName">所在包名</param>
        /// <param name="act">回调函数</param>
        /// <typeparam name="T">资源类型</typeparam>
        /// <returns></returns>
        /// <exception cref="TypeLoadException"></exception>
        public async UniTask<List<TextAsset>> LoadAllOneAssets_Async<T>(string assetPath, string pkgName = "",
            UnityAction<List<TextAsset>> act = null) => await assetsLoad_Com.LoadAllOneAssets_Async<TextAsset>(assetPath, pkgName, act);

        #endregion
    }
}